﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using LINQPad.OpenAccess.Helpers;
using Telerik.OpenAccess.Metadata;

namespace LINQPad.OpenAccess.Metadata.Reflection.Locators
{
    /// <summary>
    /// <see cref="IMetadataSourceLocator"/> for xml metadata sources.
    /// </summary>
    public class XmlMetadataSourceLocator : IMetadataSourceLocator
    {
        /// <summary>
        /// The suffix of the model metadata resources.
        /// </summary>
        public const string ResourceSuffix = ".rlinq";

        #region Private

        #region - Functions

        /// <summary>
        /// Assembles the metadata source info.
        /// </summary>
        /// <param name="resourceName">Name of the resource.</param>
        /// <param name="assembly">The assembly.</param>
        /// <returns>
        /// The metadata source info.
        /// </returns>
        private IMetadataSourceInfoWithSource AssembleMetadataSourceInfoWithSource( string resourceName, Assembly assembly )
        {
            var metadataSource = XmlMetadataSource.FromAssemblyResource( assembly, resourceName );
            var fragments = resourceName.Split( '.' );
            var name = fragments[fragments.Length - 2];

            string path = String.Empty;
            if ( fragments.Length > 2 )
                path = String.Format
                                (
                                    "{0}.{1}"
                                    , assembly.GetName( ).Name
                                    , String.Join( ".", fragments.Take( fragments.Length - 2 ) )
                                );

            return new MetadataSourceInfoWithSource( name, path, metadataSource );
        }

        /// <summary>
        /// Determines whether the specified name is a metadata resource name.
        /// </summary>
        /// <param name="resourceName">Name of the resource.</param>
        /// <returns>
        ///   <c>true</c> if the specified name is a metadata resource name; otherwise, <c>false</c>.
        /// </returns>
        private bool IsMetadataResourceName( string resourceName )
        {
            return !String.IsNullOrWhiteSpace( resourceName )
                    && resourceName.EndsWith( ResourceSuffix, StringComparison.InvariantCultureIgnoreCase );
        }

        #endregion

        #endregion

        #region IMetadataSourceLocator Members

        /// <summary>
        /// Gets the metadata source infos from the specified assembly.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns>
        /// The metadata source infos.
        /// </returns>
        public IEnumerable<IMetadataSourceInfoWithSource> GetFromAssembly( Assembly assembly )
        {
            Guard.ArgumentNotNull( assembly, "assembly" );

            var metadataSources = assembly.GetManifestResourceNames( )
                                          .Where( IsMetadataResourceName )
                                          .Select( r => AssembleMetadataSourceInfoWithSource( r, assembly ) );

            return metadataSources;
        }

        #endregion
    }
}